package gofish.dao;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;

import javax.servlet.ServletContext;

import gofish.objects.Game;
import gofish.objects.Player;

/**
 * 
 * @author Kylyn
 * 
 */

public class GoFishDAOHandler {

	private static final String[] PLAYING_CARDS = { "AH", "2H", "3H", "4H", "5H",
			"6H", "7H", "8H", "9H", "TH", "JH", "QH", "KH", "AD", "2D", "3D",
			"4D", "5D", "6D", "7D", "8D", "9D", "TD", "JD", "QD", "KD", "AC",
			"2C", "3C", "4C", "5C", "6C", "7C", "8C", "9C", "TC", "JC", "QC",
			"KC", "AS", "2S", "3S", "4S", "5S", "6S", "7S", "8S", "9S", "TS",
			"JS", "QS", "KS" };

	private GoFishDAO data;

	/**
	 * Initializes a newly created GoFishDAOHandler object.
	 * 
	 * @throws DAOException
	 */
	public GoFishDAOHandler(ServletContext context) throws DAOException {
		data = new GoFishDAO();
		data.connect(context);
	}

	@Override
	public void finalize() {
		data.disconnect();
	}

	/**
	 * Get all logged in users.
	 * 
	 * @return
	 */
	public String[] getLoggedInUsers() {
		return data.getUsers(true);
	}
	
	public String[] getLoggedOutUsers() {
		return data.getUsers(false);
	}

	/**
	 * Check if user exists.
	 * 
	 * @param username
	 * @return
	 */
	public boolean userExists(String username) {
		return !data.getUserLoggedIn(username).equals("");
	}

	/**
	 * Check if user logged in.
	 * 
	 * @param username
	 * @return
	 */
	public boolean userLoggedIn(String username) {
		return data.getUserLoggedIn(username).equals("true");
	}

	/**
	 * Login user.
	 * 
	 * @param username
	 * @return
	 */
	public boolean loginUser(String username, int g_id) {
		// if username is not logged in
		// add username to user table with logged_in=true
		// and return true
		// otherwise return false
		data.loginUser(username, g_id, userExists(username));
		return true;
	}

	/**
	 * Logout user.
	 * 
	 * @param username
	 * @return
	 */
	public boolean logoutUser(String username) {
		// if username is logged in
		// remove username from user table
		// and return true
		// otherwise return false
		data.logoutUser(username);
		return true;
	}

	/**
	 * Generate unique group ID. Add group ID to game table with e_id=0 and
	 * turn_id=0.
	 * 
	 * @param username
	 * @return group
	 */
	public int createGroup(String username) {
		int group = generateGroupID();
		data.addGroup(group, 0, 0);
		return group;
	}

	public void joinGroup(int group, String username) {
		// add if statement here.  Check to see if the list of 
		// logged in users is empty, if it is then initialize the eid
		// to 0.
		incrementEventID(group);
		loginUser(username, group);
	}

	public void leaveGroup(int group, String username) {
		
		setGameEventString(group, username + " has logged out.");
		logoutUser(username);
		incrementEventID(group);
		
		if (getGroupMembers(group) == null) {
			data.setGameTurnID(group, 0, true);
			setGameEventString(group, "");
			clearTables(group);
			resetEventID(group);
		}
	}

	/**
	 * Get list of players in group.
	 * 
	 * @param group
	 * @return
	 */
	public String[] getGroupMembers(int group) {
		return data.getGroupMembers(group);
	}

	/**
	 * Get the event ID of group.
	 * 
	 * @param group
	 * @return
	 */
	public int getEventID(int group) {
		return data.getEventID(group);
	}

	/**
	 * Increment the event ID of group.
	 * 
	 * @param group
	 */
	public void incrementEventID(int group) {
		int e_id = data.getEventID(group) + 1;
		data.setEventID(group, e_id, true);
	}
	
	/**
	 * Sets the event ID of group to zero.
	 * 
	 * @param group
	 */
	public void resetEventID(int group) {
		data.setEventID(group, 0, true);
	}

	/**
	 * Update event ID for group to -1. Generate game deck. Deal player hands.
	 * If number of players if less than 5, deal 7 cards each. Otherwise, deal 5
	 * cards each.
	 * 
	 * @param group
	 * @return
	 */
	public void startGame(int group) throws DAOException {
		// set event ID to -1 for group
		data.setEventID(group, 0, true);
		// initialize game info in database
		initGame(group);
	}

	// to do, unsure on here just yet...
	public void leaveGame() {

	}

	/**
	 * Update event ID for group to -2.
	 * 
	 * @param group
	 */
	public void endGame(int group) {
		// set event ID to -2 for group
		data.setEventID(group, -1, true);
	}

	/**
	 * Shuffle deck of group.
	 * 
	 * @param group
	 */
	public void shuffleDeck(int group) {
		List<String> deck = data.removeGameDeck(group);
		shuffleDeck(deck);
		data.addGameDeck(group, deck);
	}

	/**
	 * Update turn_id for group.
	 * 
	 * @param group
	 */
	public void nextTurn(int group) {
		int turn_id = data.getGameTurnID(group) + 1;
		int numPlayers = data.getGroupMembers(group).length;
		if (turn_id >= numPlayers) {
			turn_id = 0;
		}
		data.setGameTurnID(group, turn_id, true);
	}

	/**
	 * Get the current state of the game.
	 * 
	 * @param group
	 * @return
	 */
	public Game getGameState(int group) {
		Game game = new Game(group, data.getGameDeckSize(group));
		game.setTurn(data.getGameTurnID(group));

		String[] players = data.getUsers(true);
		for (String p : players) {
			Player player = new Player(p);
			player.setHand(data.getPlayerHand(p));
			player.setPairs(data.getPlayerPairs(p));
			game.addPlayer(player);
		}
		game.setDeckSize(data.getGameDeckSize(group));
		return game;
	}

	/**
	 * Checks if card is in player's hand.
	 * 
	 * @param group
	 * @param username
	 * @param card
	 * @return
	 */
	public boolean isCardInPlayerHand(int group, String username, String card) {
		boolean inHand = false;
		List<String> hand = data.getPlayerHand(username);
		for (String c : hand) {
			if (c.contains(card.charAt(0) + "")) {
				inHand = true;
				break;
			}
		}
		return inHand;
	}

	/**
	 * Checks if player's hand is empty.
	 * 
	 * @param group
	 * @param username
	 * @return
	 */
	public boolean isPlayerHandEmpty(int group, String username) {
		return (getPlayerHandSize(username) == 0);
	}

	/**
	 * Checks if deck of game is empty.
	 * 
	 * @param group
	 * @return
	 */
	public boolean isDeckEmpty(int group) {
		return (getGameDeckSize(group) == 0);
	}
	
	public int getGameDeckSize(int group) {
		return data.getGameDeckSize(group);
	}
	
	public void clearGameDeck(int group) {
		data.removeGameDeck(group);
	}

	/**
	 * Get card from top of deck of game.
	 * 
	 * @param group
	 * @return
	 */
	public String goFish(int group) {
		return data.removeTopCardFromDeck(group);
	}

	public void removeCardFromPlayerHand(int group, String username, String card) {
		data.removeCardFromPlayerHand(username, card);
	}

	public void addCardToPlayerHand(int group, String username, String card) {
		data.addCardToPlayerHand(username, card);
	}
	
	public void clearPlayerHand(int group) {
		data.clearPlayerHand(group);
	}

	public void addCardToPlayerPairs(int group, String username, String card) {
		data.addCardToPlayerPairs(username, card);
	}
	
	public void addCardsToPlayerPairs(int group, String username, List<String> cards) {
		for (String card : cards) {
			data.addCardToPlayerPairs(username, card);
		}
	}
	
	public void clearPlayerPairs(int group) {
		data.clearPlayerPairs(group);
	}
	
	public void clearTables(int group) {
		data.removeGameDeck(group);
		data.clearPlayerHand(0);
		data.clearPlayerPairs(0);
//		data.clearGameData(group);
	}
	
	public void setGameEventString(int group, String event) {
		data.setGameEventString(group,  event);
	}
	
	public String getGameEventString(int group) {
		return data.getGameEventString(group);
	}
	
	private int generateGroupID() {
		return 1;
	}

	private void initGame(int group) throws DAOException {
		// generate game deck
		List<String> deck = new LinkedList<String>();
		for (int i = 0; i < PLAYING_CARDS.length; i++) {
			deck.add(PLAYING_CARDS[i]);
		}
		shuffleDeck(deck);
		// get number of players
		String[] players = data.getUsers(true);

		int handSize = 0;
		// if num players less than 5
		if (players.length < 5) {
			// deal each player 7 cards
			handSize = 7;

		} else {
			// otherwise deal each player 5 cards
			handSize = 5;
		}

		int currPos = 0;
		for (int i = 0; i < players.length; i++) {
			currPos = i * handSize;
			List<String> hand = new LinkedList<String>();
			List<String> pairs = new LinkedList<String>();
			List<String> temp = copySubList(deck, currPos, currPos + handSize);
			for (String tempCard : temp) {
	            boolean match = false;
	            for (String c : temp) {
	                if (!c.equals(tempCard) && c.contains(tempCard.charAt(0) + "")) {
	                    if (!pairs.contains(tempCard)) {
	                        pairs.add(c);
	                    }
	                    match = true;
	                    break;
	                }
	            }
	            if (!match) {
	                hand.add(tempCard);
	            }
	        }
			addCardsToPlayerPairs(group, players[i], pairs);
			data.setPlayerHand(players[i], hand);
		}
		// put remainder of cards in game_deck
		List<String> gameDeck = copySubList(deck, currPos + handSize, deck.size());
		data.addGameDeck(group, gameDeck);
	}
	
	private List<String> copySubList(List<String> orig, int firstPos, int lastPos) {
		List<String> sub = orig.subList(firstPos, lastPos);
		List<String> copy = new LinkedList<String>();
		for (String s : sub) {
			copy.add(new String(s));
		}
		return copy;
	}

	private int getPlayerHandSize(String username) {
		return data.getPlayerHandSize(username);
	}

	/**
	 * Shuffle deck of "cards".
	 * 
	 * @param deck
	 * @return
	 */
	private void shuffleDeck(List<String> deck) {
		Collections.shuffle(deck);
	}

}
